package com.ruoyi.app.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.code.kaptcha.Producer;
import com.ruoyi.app.pojo.dto.UserCodeLoginDto;
import com.ruoyi.app.pojo.dto.UserGetSmsCodeDto;
import com.ruoyi.app.pojo.vo.UserGetCaptchaImageVo;
import com.ruoyi.app.pojo.vo.UserGetInfoVo;
import com.ruoyi.app.pojo.vo.UserGetSmsCodeVo;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginAppUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.pojo.entity.AppUser;
import com.ruoyi.common.sms.core.SmsEnum;
import com.ruoyi.common.sms.core.SmsResult;
import com.ruoyi.common.sms.core.SmsTemplate;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.web.service.AppTokenService;
import com.ruoyi.system.service.AppUserService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.concurrent.TimeUnit;

/**
 * ClassName：AppLoginService
 * Package：com.ruoyi.framework.web.service
 * Description：APP登录校验方法
 *
 * @Author：fpy
 * @Create：2025/5/12 - 11:21
 * @Version：v1.0
 */
@Slf4j
@Component
public class AppLoginService {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private AppTokenService appTokenService;
    @Resource
    private AppUserService appUserSevice;
    @Resource
    private RedisCache redisCache;

    @Value("${wx.min-app.app-id}")
    private String appId;
    @Value("${wx.min-app.app-secret}")
    private String appSecret;

    @Resource(name = "captchaProducer")
    private Producer captchaProducer;

    @Resource(name = "captchaProducerMath")
    private Producer captchaProducerMath;
    @Resource
    private SmsTemplate smsTemplate;

    /**
     * 验证码类型
     */
    @Value("${ruoyi.captchaType}")
    private String captchaType;

    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @return 结果
     */
    public String login(String username, String password) {
        AppUser appUser = appUserSevice.selectOrInsertAppUserByUserName(username);
        if (appUser == null) {

            throw new CustomException("用户不存在");
        }
        if (!appUserSevice.checkPassword(password, appUser.getSalt(), appUser.getPassword())) {

            throw new CustomException("密码不正确");
        }
        LoginAppUser loginAppUser = new LoginAppUser(appUser.getId(), appUser);
//        recordLoginInfo(loginAppUser.getUserId());
        // 生成token
        return appTokenService.createAppToken(loginAppUser);
    }

    /**
     * 小程序登录
     * @param code
     * @return
     */
    public String minLogin(String code) {
        String accessToken = getAccessToken();
        // 1、根据code执行小程序登录获取 openId
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;

        JSONObject requestBody = new JSONObject();
        requestBody.put("code", code);
        String responseBody = HttpUtil.post(url, JSONObject.toJSONString(requestBody));
        JSONObject responseBodyObject = JSONObject.parseObject(responseBody);
        if (!"0".equals(responseBodyObject.getString("errcode"))) {
            log.info("获取微信用户手机号失败，错误编码：{}，错误信息：{}",responseBodyObject.getString("errcode"), responseBodyObject.getString("errmsg"));
            throw new CustomException("获取手机号失败");
        }
        String phone = responseBodyObject.getJSONObject("phone_info").getString("purePhoneNumber");

        // 2、根据phone判断用户是否存在 不存在自动注册 phone对应数据库表user_name
        AppUser appUser = appUserSevice.selectOrInsertAppUserByUserName(phone);

        // 3、生成token
        LoginAppUser loginAppUser = new LoginAppUser(appUser.getId(), appUser);
        loginAppUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
        loginAppUser.setLoginTime(LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)));
//        recordLoginInfo(loginAppUser.getUserId());
        return appTokenService.createAppToken(loginAppUser);
    }

    /**
     * 获取微信access_token
     *
     * @param:
     * @return: java.lang.String
     * @author: fpy
     * @date: 2025/5/12 17:05
    **/
    private String getAccessToken() {
        String accessToken = StrUtil.str(redisCache.getCacheObject(Constants.WX_APP_ACCESS_TOKEN_KEY));
        if (StrUtil.isNotBlank(accessToken)) {
            log.info("从缓存中微信小程序access_token，accessToken:{}", accessToken);
            return accessToken;
        }

        // 调用微信接口获取access_token
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
        String replaceUrl = url.replace("{0}",appId).replace("{1}",appSecret);
        log.info("获取新的微信小程序access_token开始，url:{}", replaceUrl);
        String body = HttpUtil.get(replaceUrl);
        log.info("获取新的微信小程序access_token结果，body:{}", body);
        JSONObject jsonObject = JSONObject.parseObject(body);
        if (!jsonObject.containsKey("access_token")) {
            throw new CustomException(String.format("获取微信授权信息失败，错误编码：%s，错误信息：%s",jsonObject.getString("errcode"), jsonObject.getString("errmsg")));
        }
        accessToken = jsonObject.getString("access_token");
        Integer expiresIn = jsonObject.getInteger("expires_in");

        // 缓存access_token
        redisCache.setCacheObject(Constants.WX_APP_ACCESS_TOKEN_KEY, accessToken, expiresIn, TimeUnit.SECONDS);

        return accessToken;
    }

    /**
     * 生成图形验证码
     *
     * @param: response
     * @param: phone
     * @return: com.ruoyi.common.core.domain.AjaxResult
     * @author: fpy
     * @date: 2025/5/13 10:21
    **/
    public AjaxResult getCaptchaImage() {
        // 保存验证码信息
        String uuid = IdUtils.simpleUUID();
        String verifyKey = Constants.APP_LOGIN_CAPTCHA_CODE_KEY + uuid;

        String capStr = null, code = null;
        BufferedImage image = null;

        // 生成验证码
        if (UserConstants.CAPTCHA_TYPE_MATH.equals(captchaType)) {
            String capText = captchaProducerMath.createText();
            capStr = capText.substring(0, capText.lastIndexOf("@"));
            code = capText.substring(capText.lastIndexOf("@") + 1);
            image = captchaProducerMath.createImage(capStr);
        } else if (UserConstants.CAPTCHA_TYPE_CHAR.equals(captchaType)) {
            capStr = code = captchaProducer.createText();
            image = captchaProducer.createImage(capStr);
        }

        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
        } catch (IOException e) {
            return AjaxResult.error(e.getMessage());
        }

        UserGetCaptchaImageVo vo = new UserGetCaptchaImageVo();
        vo.setUuid(uuid);
        vo.setImg(Base64.encode(os.toByteArray()));
        return AjaxResult.success(vo);
    }

    /**
     * 获取手机验证码（需要生成图形验证码）
     *
     * @param: dto
     * @return: com.ruoyi.common.core.domain.AjaxResult
     * @author: fpy
     * @date: 2025/5/13 11:06
    **/
    public AjaxResult getSmsCode(UserGetSmsCodeDto dto) {
        // 缓存获取验证码，并对图形验证码校验
        String verifyKey = Constants.APP_LOGIN_CAPTCHA_CODE_KEY + dto.getImage_captcha_uuid();
        String code = redisCache.getCacheObject(verifyKey);
        if (!StrUtil.equalsIgnoreCase(code, dto.getImage_captcha_code())) {
            return AjaxResult.error("验证码错误");
        }

        // 向用户手机号发送短信
        String smsCode = RandomUtil.randomNumbers(6);
        SmsResult smsResult = smsTemplate.message(SmsEnum.TYPE_0, dto.getPhone(), smsCode, String.valueOf(Constants.CAPTCHA_EXPIRATION));
        if (!smsResult.getSuccess()) {
            return AjaxResult.error("短信发送失败，请稍后重试！");
        }

        // 删除图形验证码
        redisCache.deleteObject(verifyKey);

        // 将短信验证码放入缓存
        String smsUuid = IdUtil.fastSimpleUUID();
        String smsKey = Constants.APP_LOGIN_SMS_CODE_KEY + smsUuid + dto.getPhone();
        redisCache.setCacheObject(smsKey, smsCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);

        UserGetSmsCodeVo vo = new UserGetSmsCodeVo();
        vo.setUuid(smsUuid);
        return AjaxResult.success(vo);
    }

    /**
     * 短信验证码登录
     *
     * @param: dto
     * @return: java.lang.String
     * @author: fpy
     * @date: 2025/5/13 14:19
    **/
    public AjaxResult codeLogin(UserCodeLoginDto dto) {
        // 缓存获取验证码，并对短信验证码校验
        String verifyKey = Constants.APP_LOGIN_SMS_CODE_KEY + dto.getSms_uuid() + dto.getPhone();
        String code = redisCache.getCacheObject(verifyKey);
//        redisCache.deleteObject(verifyKey);
        if (!StrUtil.equalsIgnoreCase(code, dto.getSms_code())) {
            throw new CustomException("验证码错误");
        }

        // 2、根据phone判断用户是否存在 不存在自动注册 phone对应数据库表user_name
        AppUser appUser = appUserSevice.selectOrInsertAppUserByUserName(dto.getPhone());

        // 3、生成token
        LoginAppUser loginAppUser = new LoginAppUser(appUser.getId(), appUser);
        loginAppUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
        loginAppUser.setLoginTime(LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)));
        String appToken = appTokenService.createAppToken(loginAppUser);

        AjaxResult ajax = AjaxResult.success();
        ajax.put(Constants.TOKEN, appToken);
        return ajax;
    }

    /**
     * 获取用户信息
     *
     * @param:
     * @return: com.ruoyi.common.core.domain.AjaxResult
     * @author: fpy
     * @date: 2025/5/13 15:23
    **/
    public AjaxResult getInfo() {
        AppUser appUser = SecurityUtils.getLoginAppUser().getAppUser();

        UserGetInfoVo vo = new UserGetInfoVo();
        vo.setAvatar(appUser.getAvatar());
        vo.setPhone(appUser.getMobile());
        vo.setUser_name(appUser.getUserName());

        return AjaxResult.success(vo);
    }

}
